package cmdipset

import (
	"bufio"
	"context"
	"errors"
	"log"
	"strings"

	"gitee.com/daofeixian/dfx_golang_toolset/utils"
)

// Ipset 表示一个 ipset 集合的结构体
type Ipset struct {
	Name   string   // 集合名称
	Type   string   // 集合类型
	IPList []string // IP 地址列表
}

const (
	IPSET_TABLE_HASH_IP       = "hash:ip"
	IPSET_TABLE_HASH_NET      = "hash:net"
	IPSET_TABLE_HASH_IP_PORT  = "hash:ip,port"
	IPSET_TABLE_HASH_NET_PORT = "hash:net,port"
	IPSET_TABLE_BITMAP_IP     = "bitmap:ip"
	IPSET_TABLE_BITMAP_PORT   = "bitmap:port"
	IPSET_TABLE_LIST_SET      = "list:set"
)

// CheckIpsetCmd 检查系统中是否安装了 ipset 命令
// 参数:
//   - ctx: 上下文，用于控制命令执行的生命周期
// 返回值:
//   - error: 如果未找到 ipset 命令则返回错误，否则返回 nil
func CheckIpsetCmd(ctx context.Context) error {
	err := utils.CmdDo(ctx, "bash", "-c", "command -v ipset  >> /dev/null || which ipset >> /dev/null")
	if err != nil {
		return err
	}
	return nil
}

// checkTableType 校验给定的集合类型是否为合法的 ipset 类型
// 参数:
//   - tableType: 待校验的集合类型字符串
// 返回值:
//   - error: 如果类型不合法则返回错误，否则返回 nil
func checkTableType(tableType string) error {
	switch tableType {
	case IPSET_TABLE_HASH_IP, IPSET_TABLE_HASH_NET, IPSET_TABLE_HASH_IP_PORT, IPSET_TABLE_HASH_NET_PORT, IPSET_TABLE_BITMAP_IP, IPSET_TABLE_BITMAP_PORT, IPSET_TABLE_LIST_SET:
		return nil
	default:
		return errors.New("ipset table type error")
	}
}

// CreateTable 创建一个新的 ipset 集合
// 参数:
//   - ctx: 上下文，用于控制命令执行的生命周期
// 返回值:
//   - error: 如果创建失败则返回错误，否则返回 nil
func (i *Ipset) CreateTable(ctx context.Context) error {
	err := checkTableType(i.Type)
	if err != nil {
		return err
	}
	err = utils.CmdDo(ctx, "ipset", "create", i.Name, i.Type)
	if err != nil {
		return err
	}
	return nil
}

// CheckTable 检查指定名称的 ipset 集合是否存在
// 参数:
//   - ctx: 上下文，用于控制命令执行的生命周期
// 返回值:
//   - bool: 如果存在返回 true，否则返回 false
//   - error: 如果执行出错则返回错误，否则返回 nil
func (i *Ipset) CheckTable(ctx context.Context) (bool, error) {
	output, _, err := utils.CmdOutput(ctx, "ipset", "list")
	if err != nil {
		return false, err
	}
	if strings.Contains(string(output), i.Name) {
		return true, nil
	}
	return false, nil
}

// QueryTable 查询指定 ipset 集合中的所有成员
// 参数:
//   - ctx: 上下文，用于控制命令执行的生命周期
// 返回值:
//   - []string: 成员列表
//   - error: 如果查询失败则返回错误，否则返回 nil
func (i *Ipset) QueryTable(ctx context.Context) ([]string, error) {
	output, errput, err := utils.CmdOutput(ctx, "ipset", "list", i.Name)
	if err != nil {
		log.Printf("errput: %s,err: %s", errput, err)
		return nil, err
	}

	// 解析 Members: 段落后的所有成员行
	var members []string
	scanner := bufio.NewScanner(strings.NewReader(output))
	inMembers := false
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		if !inMembers {
			if strings.HasPrefix(line, "Members:") {
				inMembers = true
			}
			continue
		}
		if line == "" {
			// 跳过空行
			continue
		}
		if strings.HasPrefix(line, "Name:") {
			// 出现下一个集合，停止解析
			break
		}
		// 去除可能的前缀符号
		line = strings.TrimPrefix(line, "- ")
		members = append(members, line)
	}
	if scanErr := scanner.Err(); scanErr != nil {
		return nil, scanErr
	}
	return members, nil
}

// DeleteTable 删除指定名称的 ipset 集合
// 参数:
//   - ctx: 上下文，用于控制命令执行的生命周期
// 返回值:
//   - error: 如果删除失败则返回错误，否则返回 nil
func (i *Ipset) DeleteTable(ctx context.Context) error {
	err := utils.CmdDo(ctx, "ipset", "destroy", i.Name)
	if err != nil {
		return err
	}
	return nil
}

// FlushTable 清空指定 ipset 集合中的所有成员
// 参数:
//   - ctx: 上下文，用于控制命令执行的生命周期
// 返回值:
//   - error: 如果清空失败则返回错误，否则返回 nil
func (i *Ipset) FlushTable(ctx context.Context) error {
	err := utils.CmdDo(ctx, "ipset", "flush", i.Name)
	if err != nil {
		return err
	}
	return nil
}

// AddIPItem 向指定 ipset 集合中添加 IP 列表中的所有项
// 参数:
//   - ctx: 上下文，用于控制命令执行的生命周期
// 返回值:
//   - error: 如果添加失败则返回错误，否则返回 nil
func (i *Ipset) AddIPItem(ctx context.Context) error {
	for _, v := range i.IPList {
		err := utils.CmdDo(ctx, "ipset", "add", i.Name, v)
		if err != nil {
			return err
		}
	}
	return nil
}

// DelIPItem 从指定 ipset 集合中删除 IP 列表中的所有项
// 参数:
//   - ctx: 上下文，用于控制命令执行的生命周期
// 返回值:
//   - error: 如果删除失败则返回错误，否则返回 nil
func (i *Ipset) DelIPItem(ctx context.Context) error {
	for _, v := range i.IPList {
		err := utils.CmdDo(ctx, "ipset", "del", i.Name, v)
		if err != nil {
			return err
		}
	}
	return nil
}